
#include "stdafx.h"
#include "dquad.h"
#include "CImg.h"
#include "gl/glew.h"

#include <string>
#include <cstdio>

using namespace std;
using namespace cimg_library;

DQuad::DQuad(const string& file)
{
#if 0
    FILE* fIn=fopen(file.c_str(), "rb");

    int dims[2];
    fread(dims, sizeof(dims), 1, fIn);
    float* texDataRaw=new float[dims[0]*dims[1]];
    fread(texDataRaw, sizeof(float), dims[0]*dims[1], fIn);
    CImg<float> texData(texDataRaw, dims[0], dims[1]);
    delete[] texDataRaw;
    fclose(fIn);
#else
    CImg<unsigned char> texData("assets/shaders_offest_normalmap.jpg");
    CImg<unsigned char> texHeightMap("assets/shaders_offest_heightmap.jpg");
    texHeightMap.channel(0);
    texData.append(texHeightMap, 'c');
    CImg<unsigned char> texDiff("assets/shaders_offest_diffuse.jpg");
    CImg<unsigned char> texSpec("assets/shaders_offest_specular.jpg");
    texSpec.channel(0);
    texDiff.append(texSpec, 'c');
#endif

    glGenTextures(2, m_texs);
    glActiveTexture(GL_TEXTURE0);

    texDiff.mirror('y');
    int texWidht=texDiff.width();
    int texHeight=texDiff.height();
    texDiff.permute_axes("cxyz");
    glBindTexture(GL_TEXTURE_2D, m_texs[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidht, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDiff.data());
    glGenerateMipmap(GL_TEXTURE_2D);

    texData.mirror('y');
    texWidht=texData.width();
    texHeight=texData.height();
    texData.permute_axes("cxyz");
    glBindTexture(GL_TEXTURE_2D, m_texs[1]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidht, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData.data());
    glGenerateMipmap(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, 0);

    const float rotAngle=-M_PI/2;
    const float a=sin(rotAngle*.5f), b=cos(rotAngle*.5f);
    m_modelData.ModelView=RotationMatrixFromQ(vec4(a,0,0,b));
    m_modelData.ModelView(1,3)=-2.0f;
    m_modelData.ModelView(2,3)=-5.0f;
    m_modelData.ModelView.NormalMatrix(m_modelData.NormalM);
    m_materialData.diffuse[2]=.8f;  // b channel
    m_materialData.diffuse[3]=1.5f; // roughness
    m_materialData.extra[0]=.2f;    // height scale
    m_materialData.extra[1]=2.0f;   // Su (or width)
    m_materialData.extra[2]=2.0f;   // Sv (or height)

    glGenVertexArrays(1, &m_vaoId);

    glGenBuffers(2, m_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo[0]);
    const GLfloat vArr[] = {
        -1.0f, -1.0f, 0.0f, 1.0f,
        1.0f, -1.0f, 0.0f, 1.0f,
        -1.0f, 1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 0.0f, 1.0f,
    };
    glBufferData(GL_ARRAY_BUFFER, sizeof(vArr), vArr, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo[1]);
    const GLfloat vTexArr[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };
    glBufferData(GL_ARRAY_BUFFER, sizeof(vTexArr), vTexArr, GL_STATIC_DRAW);

    glGenBuffers(1, &m_vIndx);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vIndx);
    const unsigned short vIndxArr[] = {
        0, 1, 2, 3
    };
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vIndxArr), vIndxArr, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

DQuad::~DQuad()
{
    glDeleteTextures(2, m_texs);
    glDeleteBuffers(2, m_vbo);
    glDeleteBuffers(1, &m_vIndx);
    glDeleteVertexArrays(1, &m_vaoId);
}

void DQuad::AttachUniforms(const UniformMap& uBlocks)
{
    m_uniformModel=static_cast<const ModelBlock*>(uBlocks.at("Model"));
    m_uniformMaterial=static_cast<const MaterialBlock*>(uBlocks.at("MaterialAttrs"));
}

void DQuad::MapAttributes(const ProgramsMap& tMap)
{
    const IProgram* pEffect=tMap.at("TessPresent");

    glBindVertexArray(m_vaoId);

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo[0]);
    m_Position=pEffect->GetAttribLocation("Position");
    glVertexAttribPointer(m_Position, 4, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(m_Position);

    glBindBuffer(GL_ARRAY_BUFFER, m_vbo[1]);
    m_TexCoords=pEffect->GetAttribLocation("TexCoord");
    glVertexAttribPointer(m_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(m_TexCoords);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void DQuad::Draw(const PresentTechnique* pTechnique)
{
    m_uniformModel->SetData(&m_modelData);
    m_uniformModel->Update();
    m_uniformMaterial->SetData(&m_materialData);
    m_uniformMaterial->Update();

    static_cast<const DisplacedQuadTechnique*>(pTechnique)->SetImgTexture(m_texs[0]);
    static_cast<const DisplacedQuadTechnique*>(pTechnique)->SetDispTexture(m_texs[1]);

    glBindVertexArray(m_vaoId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vIndx);
    glPatchParameteri(GL_PATCH_VERTICES, 4);
    //glDrawArrays(GL_PATCHES, 0, 4);
    glDrawElements(GL_PATCHES, 4, GL_UNSIGNED_SHORT, NULL);
}

MeshPresentMethod DQuad::PresentMethod() const
{
    return PRESENT_TESSELATED;
}
